package mediator;

import guiWindows.GUIDiagram;
import guiWindows.MainWindow;
import guiWindows.PanelArea;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;

import objectsDiagram.Condition;
import objectsDiagram.Flow;
import objectsDiagram.Property;
import objectsDiagram.Fluent;
import objectsDiagram.Task;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

public class MainMediator implements ActionListener, TreeSelectionListener{

	private MainWindow mainWindow;
	private String yawlFile;
	private ReadXML readXML;
	private LinkedList<Fluent> listFluents;
	private LinkedList<Property> listProperties;
	private LinkedList<GUIDiagram> listGUIDiagrams;
	private File path;
	private JFileChooser fileChooserSave;
	private JFileChooser fileChooserOpen;
	private GUIDiagram guiDiagram;
	private JFileChooser fileChooserOpenProperty;
	private String currentNameFile;
	private AddPropertyMediator addPropertyMediator;
	private Vector<Condition> vectorCondition;
	private Vector<Flow> vectorFlow;
	private Vector<Task> vectorTask;

	public MainMediator(){
		this.vectorTask = new Vector<Task>();
		this.vectorCondition = new Vector<Condition>();
		this.vectorFlow = new Vector<Flow>();
		this.listGUIDiagrams = new LinkedList<GUIDiagram>();

		this.listFluents = new LinkedList<Fluent>();
		this.listProperties = new LinkedList<Property>();
		this.path = new File("");
		this.addPropertyMediator = new AddPropertyMediator(vectorTask, listFluents, yawlFile);
		this.mainWindow = new MainWindow(addPropertyMediator.getGuiCreateProperties());

		this.mainWindow.setListenerButtons(this);
		this.mainWindow.setTreeSellectionListener(this);
		this.mainWindow.setListFluent(listFluents);
		this.mainWindow.getItemNewFluent().setEnabled(false);
		this.mainWindow.getItemDeleteFluent().setEnabled(false);
		this.mainWindow.getItemSelectTempletes().setEnabled(false);
		this.mainWindow.getItemOpenProperty().setEnabled(false);
		this.mainWindow.getItemSaveProperty().setEnabled(false);
		this.mainWindow.getItemYawl2Fsp().setEnabled(false);
		this.addPropertyMediator.setMainWindow(this.mainWindow);
	}

	public void actionPerformed(ActionEvent e) {
		if(mainWindow.getItemSelectTempletes() == e.getSource()){		
			this.selectTemplates();
		}
		if(mainWindow.getItemOpenFile() == e.getSource()){		
			this.openYAWLFile();
		}
		if(mainWindow.getItemSaveProperty() == e.getSource()){
			this.saveProperty();
		}
		if(mainWindow.getItemOpenProperty()==e.getSource()){
			this.openProperty();
		}
		if(mainWindow.getItemNewFluent()==e.getSource()){
			this.newFluent();
		}
		if(mainWindow.getItemDeleteFluent()==e.getSource()){
			this.deleteFluent();
		}
		if(mainWindow.getItemYawl2Fsp()==e.getSource()){
			this.runYawl2Fsp();
		}
		if(mainWindow.getButtonCursorSellection()==e.getSource()){
			mainWindow.setSellectionMouse();
		}
		if(mainWindow.getButtonCursorOnFluent()==e.getSource()){
			mainWindow.setOnMouse();
		}
		if(mainWindow.getButtonCursorOffFluent()==e.getSource()){
			mainWindow.setOffMouse();
		}
		if(mainWindow.getButtonCursorEraser()==e.getSource()){
			mainWindow.setEraserMouse();
		}

	}

	private void runYawl2Fsp() {
		//		try {
		//			String [] cmd = {"java","-jar","yawl2fsp.jar", yawlFile};
		//			Runtime.getRuntime().exec(cmd);
		//			GUIYawl2Fsp guiYawl2Fsp = new GUIYawl2Fsp();
		//	        StringBuffer text = new StringBuffer();
		//	        text.append(fillingFluents());
		//	        try {
		//				File file = new File ("output.lts");
		//				FileReader fr = new FileReader (file);
		//		        int c;
		//		        while((c=fr.read())!=-1){
		//		        	text.append((char)c);
		//		        }
		//		        fr.close();
		//		        guiYawl2Fsp.getYawl2fspArea().setText(text.toString());
		//		        guiYawl2Fsp.setVisible(true);
		//		    }catch(Exception e){
		//		    	e.printStackTrace();
		//		    }
		//		} catch (IOException ioe) {
		//			ioe.printStackTrace();
		//		}
	}

	public void selectTemplates(){
		TemplateMediator templateMediator = new TemplateMediator(vectorTask,listFluents,this.mainWindow);
	}

	public void collectTaskFromDiagram(){
		for(int i=0;i<listGUIDiagrams.size();i++){
			vectorTask.addAll(listGUIDiagrams.get(i).getDiagram().getTasks());
		}
	}
	
	public void collectConditionFromDiagram(){
		for(int i=0;i<listGUIDiagrams.size();i++){
			vectorCondition.addAll(listGUIDiagrams.get(i).getDiagram().getConditions());
		}
	}
	
	public void collectFlowFromDiagram(){
		for(int i=0;i<listGUIDiagrams.size();i++){
			vectorFlow.addAll(listGUIDiagrams.get(i).getDiagram().getFlows());
		}
	}

	public void deleteFluent(){
		Fluent fluent = mainWindow.getSelectedFluent();
		for(int i=0;i<listFluents.size();i++){
			if(fluent.getId().compareTo(listFluents.get(i).getId())==0){
				listFluents.remove(i);
			}
		}
		mainWindow.deleteFluent();
		if(listFluents.size()==0){
			mainWindow.setIndexForFluentId(0);
			mainWindow.getButtonCursorOffFluent().setEnabled(false);
			mainWindow.getButtonCursorOnFluent().setEnabled(false);
			mainWindow.getButtonCursorEraser().setEnabled(false);
			mainWindow.getItemDeleteFluent().setEnabled(false);
			setFluentToDiagram(null);
		}
	}

	public void newFluent(){
		Fluent fluent = new Fluent();
		fluent.setId("");
		listFluents.add(fluent);
		setFluentToDiagram(fluent);
		mainWindow.addNewFluent(fluent);
		mainWindow.getButtonCursorOffFluent().setEnabled(true);
		mainWindow.getButtonCursorOnFluent().setEnabled(true);
		mainWindow.getButtonCursorEraser().setEnabled(true);
		mainWindow.getItemDeleteFluent().setEnabled(true);
	}

	public void openYAWLFile(){
		if (fileChooserOpen == null){
			fileChooserOpen = new JFileChooser();
		}
		FileNameExtensionFilter filter = new FileNameExtensionFilter("YAWL Model","yawl");
		fileChooserOpen.setFileFilter(filter);
		fileChooserOpen.setCurrentDirectory(path);
		fileChooserOpen.setDialogTitle("Choose a YAWL file");
		int retVal = fileChooserOpen.showOpenDialog(fileChooserOpen);
		if (retVal == JFileChooser.APPROVE_OPTION){
			this.yawlFile = fileChooserOpen.getSelectedFile().getAbsolutePath();
			this.currentNameFile = fileChooserOpen.getSelectedFile().getName();
			this.mainWindow.setVisible(false);
			this.listGUIDiagrams.clear();
			this.mainWindow.clearDiagram();
			this.mainWindow.clearFluents();
			this.listFluents.clear();
			this.vectorTask.clear();
			this.vectorCondition.clear();
			this.vectorFlow.clear();
			this.mainWindow.clearProperties();
			this.listProperties.clear();
			try{
				this.readXML = new ReadXML();
				this.readXML.parseXMLtoDiagram(yawlFile);
				for(int i =0;i<readXML.getListDiagrams().size();i++){
					this.guiDiagram = new GUIDiagram(readXML.getListDiagrams().get(i),mainWindow);
					this.listGUIDiagrams.add(guiDiagram);
					this.mainWindow.loadDiagram(guiDiagram);
				}
				this.mainWindow.setListGUIDiagrams(this.listGUIDiagrams);
				this.mainWindow.getItemNewFluent().setEnabled(true);
				this.mainWindow.getItemSelectTempletes().setEnabled(true);
				this.mainWindow.getItemOpenProperty().setEnabled(true);
				this.mainWindow.getItemSaveProperty().setEnabled(true);
				this.mainWindow.getItemYawl2Fsp().setEnabled(true);
				this.mainWindow.getGuiCreateProperties().getAddButton().setEnabled(true);

				this.mainWindow.setVisible(true);
			}catch(Exception e){
				e.printStackTrace();
				JOptionPane.showMessageDialog(null,"ERROR", "ERROR OPEN YAWL FILE", JOptionPane.INFORMATION_MESSAGE);
			}
		}
		collectTaskFromDiagram();
		collectConditionFromDiagram();
		collectFlowFromDiagram();
		this.addPropertyMediator.setVectorTask(this.vectorTask);
		this.addPropertyMediator.setVectorCondition(this.vectorCondition);
		this.addPropertyMediator.setVectorFlow(this.vectorFlow);
		this.addPropertyMediator.setYawlFile(yawlFile);
	}

	private void saveProperty(){
		if (fileChooserSave == null){
			fileChooserSave = new JFileChooser();
		}
		File newFile = new File(yawlFile.substring(0,yawlFile.length()-5)+".ffp");
		fileChooserSave.setCurrentDirectory(path);
		fileChooserSave.setDialogTitle("Save YAWL model's property");
		int retVal = fileChooserSave.showSaveDialog(fileChooserSave);
		if (retVal == fileChooserSave.APPROVE_OPTION){			
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			try{
				newFile = new File(fileChooserSave.getSelectedFile().getCanonicalPath()+".ffp");
				DocumentBuilder builder = factory.newDocumentBuilder();
				DOMImplementation implementation = builder.getDOMImplementation();
				Document document = implementation.createDocument(null, "xml", null);
				Element mainFile = document.createElement("file-attributes");
				mainFile.setAttribute("name", currentNameFile);
				document.getDocumentElement().appendChild(mainFile);
				//Adding fluents to the xml file
				Element fluents = document.createElement("fluents");
				for(int i = 0;i<listFluents.size();i++){
					Element fluent = document.createElement("fluent");
					fluent.setAttribute("id", listFluents.get(i).getId());
					Element	onTask = document.createElement("on");
					fluent.appendChild(onTask);
					for(int j = 0;j<listFluents.get(i).getListOn().size();j++){
						Element fluentTask = document.createElement("fluent_task");
						fluentTask.setAttribute("id", ((Task)(listFluents.get(i).getListOn().get(j)[0])).getId()+"");
						fluentTask.setAttribute("condition", (listFluents.get(i).getListOn().get(j)[1])+"");
						//Text elementOnTask = document.createTextNode();
						onTask.appendChild(fluentTask);
					}
					Element	offTask = document.createElement("off"); 
					fluent.appendChild(offTask);
					for(int k = 0;k<listFluents.get(i).getListOff().size();k++){
						Element fluentTask = document.createElement("fluent_task");
						fluentTask.setAttribute("id", ((Task)(listFluents.get(i).getListOff().get(k)[0])).getId()+"");
						fluentTask.setAttribute("condition", (listFluents.get(i).getListOff().get(k)[1])+"");
						offTask.appendChild(fluentTask);
					}
					document.setXmlVersion("1.0");
					mainFile.appendChild(fluents);
					fluents.appendChild(fluent);
				}

				//Addind the Properties in the xml file
				if(this.mainWindow.getGuiCreateProperties().getPanelAreas().size()>0){
					Element properties = document.createElement("properties");
					mainFile.appendChild(properties);
					for(int i=0; i<this.mainWindow.getGuiCreateProperties().getPanelAreas().size();i++){
						Element property = document.createElement("property");
						PanelArea panelArea = this.mainWindow.getGuiCreateProperties().getPanelAreas().get(i);
						property.setAttribute("id", String.valueOf(i));
						property.setAttribute("property", panelArea.getArea().getText());
						property.setAttribute("enabled", String.valueOf(panelArea.getCheckBox().isSelected()));
						properties.appendChild(property);
					}
				}

				Source source = new DOMSource(document);
				Result result = new StreamResult(newFile);

				Transformer transformer = TransformerFactory.newInstance().newTransformer();
				transformer.transform(source, result);

			} catch (IOException e) {
				JOptionPane.showMessageDialog(null, "Error on the file creation");
				e.printStackTrace();

			}catch(Exception er){
				er.printStackTrace();
			}
		}
	}

	private void openProperty(){
		if (fileChooserOpenProperty == null){
			fileChooserOpenProperty = new JFileChooser();
		}
		FileNameExtensionFilter filter = new FileNameExtensionFilter("File Fluent Property","ffp");
		fileChooserOpenProperty.setCurrentDirectory(path);
		fileChooserOpenProperty.setFileFilter(filter);
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			int returnVal = fileChooserOpenProperty.showOpenDialog(fileChooserOpenProperty);
			if(returnVal == JFileChooser.APPROVE_OPTION) {
				File fileConstrint = fileChooserOpenProperty.getSelectedFile();
				Document document = db.parse(fileConstrint);
				document.getDocumentElement().normalize();

				NodeList nodeMainFile = document.getElementsByTagName("file-attributes");
				String nameMainFile;
				if(nodeMainFile.getLength()>0){
					Node mainFile = nodeMainFile.item(0);
					Element elementMainFile = (Element)mainFile;

					NamedNodeMap nameMapMainFile = elementMainFile.getAttributes();
					Node nameAttr = nameMapMainFile.getNamedItem( "name" );
					nameMainFile = nameAttr.getNodeValue();
					System.out.println(""+yawlFile+"/"+nameMainFile);
					if(nameMainFile.compareTo(currentNameFile)==0){
						try{
							int reply = 0;
							if(this.mainWindow.getGuiCreateProperties().getListPanelAreas().size()>0 || this.listFluents.size()>0){
								reply = JOptionPane.showConfirmDialog(null, "The current model has changes and will be erased. Do you like proced?", "Open Property", JOptionPane.YES_NO_OPTION);
								if (reply == JOptionPane.YES_OPTION){
									this.mainWindow.clearFluents();
									this.listFluents.clear();
									this.mainWindow.clearProperties();
									this.listProperties.clear();
								}
							}
							if(reply == ConstantsModel.ACEPT_OPEN_PREPERTY_FILE){
								openFluents(document);
								openProperties(document);
							}
						}catch(Exception e){
							e.printStackTrace();
							JOptionPane.showMessageDialog(null, "Error in open the file. The file is broken");
						}
					}else{JOptionPane.showMessageDialog(null, "Wrong file opened. \nNot matching with the YAWL opened");}
				}
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void setFluentToDiagram(Fluent fluent){
		for(int i =0;i<listGUIDiagrams.size();i++){
			listGUIDiagrams.get(i).setCurrentFluent(fluent);
			listGUIDiagrams.get(i).repaint();
		}
	}

	public void valueChanged(TreeSelectionEvent e) {
		DefaultMutableTreeNode node = (DefaultMutableTreeNode)mainWindow.getTree().getLastSelectedPathComponent();
		if(node != null){
			Object nodeInfo = node.getUserObject();
			if (!node.isLeaf()) {
				String fluetString = (String)nodeInfo;
				String fluentId = fluetString.substring(fluetString.indexOf(":")+1, fluetString.length());
				for(int i =0;i<listFluents.size();i++){
					if(listFluents.get(i).getId().compareTo(fluentId)==0){
						mainWindow.setCurrentFluent(listFluents.get(i));
						setFluentToDiagram(listFluents.get(i));
					}
				}
			}
		}
	}

	private void openFluents(Document document){
		NodeList nodeFluents = document.getElementsByTagName("fluent");
		for(int i=0;i<nodeFluents.getLength();i++){
			Node nodeFluent = nodeFluents.item(i);
			Element elementFluent = (Element)nodeFluent;
			Fluent fluent = new Fluent();

			//setting id
			NamedNodeMap namedMapFluent = elementFluent.getAttributes();
			Node idAttr = namedMapFluent.getNamedItem( "id" );
			String idFluent = idAttr.getNodeValue();
			fluent.setId(idFluent);

			//setting task On
			NodeList nodeListTasksOn = elementFluent.getElementsByTagName("on");
			if(nodeListTasksOn.getLength()>0){
				Node nodeTaskOn =  nodeListTasksOn.item(0);
				Element elementNodeTaskOn = (Element)nodeTaskOn;

				NodeList listFluentTaskOn = elementNodeTaskOn.getElementsByTagName("fluent_task");
				for(int j=0;j<listFluentTaskOn.getLength();j++){
					Node nodeFluentTaskOn = listFluentTaskOn.item(j);
					Element elementTaskOn = (Element)nodeFluentTaskOn;
					Object[] task = {null,null,null};

					//searching task in vector of tasks
					NamedNodeMap namedMapTaskOn = elementTaskOn.getAttributes();
					Node idTaskAttr = namedMapTaskOn.getNamedItem( "id" );
					String idTaskOn = idTaskAttr.getNodeValue();

					for(int k=0;k<vectorTask.size();k++){
						Task taskOn = vectorTask.get(k);
						if(idTaskOn.compareTo(taskOn.getId())==0){
							task[0] = taskOn;
							break;
						}
					}

					Node conditionTaskAttr = namedMapTaskOn.getNamedItem( "condition" );
					String conditionTaskOn = conditionTaskAttr.getNodeValue();
					task[1] = Boolean.valueOf(conditionTaskOn);

					for(int s=0;s<readXML.getListDiagrams().size();s++){
						if(readXML.getListDiagrams().get(s).getTasks().contains(task[0])){
							task[2] = readXML.getListDiagrams().get(s).getId();
							break;
						}
					}
					fluent.addOn(task);
				}
			}
			//setting task Off
			NodeList nodeListTasksOff = elementFluent.getElementsByTagName("off");
			if(nodeListTasksOff.getLength()>0){
				Node nodeTaskOff =  nodeListTasksOff.item(0);
				Element elementNodeTaskOff = (Element)nodeTaskOff;

				NodeList listFluentTaskOff = elementNodeTaskOff.getElementsByTagName("fluent_task");
				for(int j=0;j<listFluentTaskOff.getLength();j++){
					Node nodeFluentTaskOff = listFluentTaskOff.item(j);
					Element elementTaskOff = (Element)nodeFluentTaskOff;
					Object[] task = {null,null,null};

					//searching task in vector of tasks
					NamedNodeMap namedMapTaskOff = elementTaskOff.getAttributes();
					Node idTaskAttr = namedMapTaskOff.getNamedItem( "id" );
					String idTaskOff = idTaskAttr.getNodeValue();

					for(int k=0;k<vectorTask.size();k++){
						Task taskOff = vectorTask.get(k);
						if(idTaskOff.compareTo(taskOff.getId())==0){
							task[0] = taskOff;
							break;
						}
					}										
					Node conditionTaskAttr = namedMapTaskOff.getNamedItem( "condition" );
					String conditionTaskOff = conditionTaskAttr.getNodeValue();
					task[1] = Boolean.valueOf(conditionTaskOff);

					for(int s=0;s<readXML.getListDiagrams().size();s++){
						if(readXML.getListDiagrams().get(s).getTasks().contains(task[0])){
							task[2] = readXML.getListDiagrams().get(s).getId();
							break;
						}
					}
					fluent.addOff(task);
				}
			}
			listFluents.add(fluent);
			setFluentToDiagram(fluent);
			mainWindow.addNewFluent(fluent);
			mainWindow.getButtonCursorOffFluent().setEnabled(true);
			mainWindow.getButtonCursorOnFluent().setEnabled(true);
			mainWindow.getButtonCursorEraser().setEnabled(true);
			mainWindow.getItemDeleteFluent().setEnabled(true);
		}
	}

	private void openProperties(Document document){
		NodeList nodeProperties = document.getElementsByTagName("property");
		for(int i=0;i<nodeProperties.getLength();i++){
			Node nodeProperty = nodeProperties.item(i);
			Element elementProperty = (Element)nodeProperty;
			NamedNodeMap namedMapProperty = elementProperty.getAttributes();

			Node enabledAttr = namedMapProperty.getNamedItem( "enabled" );
			String enabledProperty = enabledAttr.getNodeValue();


			Node idAttr = namedMapProperty.getNamedItem( "id" );
			String idString = idAttr.getNodeValue();
			
			Node propertyAttr = namedMapProperty.getNamedItem( "property" );
			String propProperty = propertyAttr.getNodeValue();

			this.mainWindow.getGuiCreateProperties().addNewAreaPanel();
			PanelArea panelArea = this.mainWindow.getGuiCreateProperties().getPanelAreas().getLast();
			boolean isEnabled = Boolean.valueOf(enabledProperty);
			int id = Integer.parseInt(idString);
			panelArea.getCheckBox().setSelected(isEnabled);
			Property currentProperty = new Property(id, propProperty, isEnabled);
			this.listProperties.add(currentProperty);
			
			panelArea.getArea().setText(propProperty);
			this.mainWindow.getGuiCreateProperties().setVisible(false);
			this.mainWindow.getGuiCreateProperties().setVisible(true);
			this.mainWindow.getGuiCreateProperties().getDeleteButton().setEnabled(true);
			this.mainWindow.getGuiCreateProperties().getCheckPropertyButton().setEnabled(true);
		}
	}
}
